Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix unhandled UnsupportedOperationException in Fallocate #1010

Open
wants to merge 3 commits into
base: next
Choose a base branch
from

Conversation

torusrxxx
Copy link
Contributor

Sorry, there were some issues that GitHub does not let me to update the branch with changes to GitHub actions, so I recreated #1008. Original indentation style is restored. fd with the value 0 is never actually used. I'm not sure what is unsafe to write a byte at the end of a file on Windows. The data written is not going to be used other than to allocate disk space, and filling the file with random data instead of zeros can shorten SSD life time. I have run this code on Windows.

}

public Fallocate fromOffset(long offset) {
public Fallocate fromOffset(long offset) throws IllegalArgumentException {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's very uncommon to list unchecked exceptions in the throws clause. The compiler does not benefit from it in any way.

Let's not introduce this as a new pattern.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This unhandled UnsupportedOperationException bug could probably be prevented if the code author sees the throws clause in the IDE, so I would like to have it when the list is not too long.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If that's the goal, please add Javadoc. Java throws is for the compiler, Javadoc @throws is for the developer. As an added benefit, it allows you to describe in which situations the exception is thrown!

try {
return new Fallocate(channel, getDescriptor(channel), final_filesize);
} catch (final UnsupportedOperationException exception) {
// File descriptor is not supported: fd is null and unavailable
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This solves the problem in the wrong place. The entire try/catch dance can be avoided by not throwing UnsupportedOperationException in getDescriptor to start with. Just have it return 0 (like you are simulating here) or -1 (as is done in UdpSocketHandler).

errno = result == 0 ? 0 : Native.getLastError();
} else if (IS_POSIX) {
errno = FallocateHolderPOSIX.posix_fallocate(fd, offset, final_filesize-offset);
if (fd != 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be more robust to check for fd > 2 instead of fd != 0, as we can be quite sure the fd will not be stdout or stderr either.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We just need to remember that the UnsupportedOperationException was thrown. And fd > 2 does not mean anything neither stdout nor stderr, what about negative numbers? That seems unnecessary.

Copy link
Contributor

@bertm bertm Nov 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On POSIX systems, file descriptors are non-negative numbers. The file descriptors 0, 1 and 2 represent stdin, stdout and stderr respectively. See https://en.wikipedia.org/wiki/File_descriptor for an explanation.

(And non-POSIX systems we don't care about here, as fallocate is not supported on them anyways).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, if there are negative numbers or 0, 1 and 2, then it must be non-POSIX systems, so legacyFill must be used.

isUnsupported = true;
// fd is null and unavailable
if (Platform.isWindows()) {
// Windows do not create sparse files by default, so just write a byte at the end.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The point of fallocate is to efficiently pre-allocate disk space for sparse files (allocate the blocks, but don't write the zeroes).

If Windows does not create sparse files anyways, then why do we need this special case? We already deal with the unsupported case through legacyFill below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

legacyFill fills the file with random data. It's wasting SSD life time because when the useful data get in, the random data have to be erased. However it does not need to erase when writing zeros. So I suggest legacyFill should fill the file with zeros. And for Windows, this is better, because there's only one API call and does not use a large buffer, so it's faster (suggested by https://stackoverflow.com/questions/18031841/pre-allocating-drive-space-for-file-storage referenced in this file).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on filesystem (and most notably full-disk encryption) the SSD erase argument would indeed be a valid one.

As far as I remember, the "fill with random data" approach was to prevent transparent disk compression from getting in the way of allocating sufficient space. But it may have become less relevant over time with ever growing disk capacity.

If we keep this special case for Windows, can you it to where the unsupported case is handled? (probably where legacyFill() is called) Then it is clear that this is not an equivalent of fallocate, but rather an optimized alternative for when fallocate is unsupported.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants